/*************************************************************************
 *
 *  Copyright (C) 2013 Florian Dang <florian.coin@gmail.com>
 *
 *  This file is part of NatusVerse.
 *
 *  NatusVerse is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  NatusVerse is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with NatusVerse.  If not, see <http://www.gnu.org/licenses/>.
 *
 *************************************************************************/

#ifndef INC_RAYTRACER_OBJECTS_POINT_H_
#define INC_RAYTRACER_OBJECTS_POINT_H_

/*!
 * \file        point.h
 * \brief       Point/Vector
 * \author      Florian Dang
 * \date        2013
 */

#include <iostream>
#include <vector>

namespace raytracer
{
//! Point/vector class
/*!
  Defines a point or vector with its 3 coordinates
*/
class Point
{
	public:
		//! point default constructor at space origin
		Point() : x_(0), y_(0), z_(0) {}

		//! classic point default constructor
		Point(float x0, float y0, float z0)
			: x_(x0), y_(y0), z_(z0) {}

		//! point destructor
		~Point() {}

		//! x accessor
		inline const float& x() const { return x_; }
		//! y accessor
		inline const float& y() const { return y_; }
		//! z accessor
		inline const float& z() const { return z_; }

		void  set(float f) { x_ = f; y_ = f; z_ = f; }
		void  set(float x0, float y0, float z0) { x_= x0; y_ = y0; z_ = z0; }

		//! x mutator
		void  set_x(float x0) { x_ = x0; }
		//! y mutator
		void  set_y(float y0) { y_ = y0; }
		//! z mutator
		void  set_z(float z0) { z_ = z0; }

		//! point I/O display
		void  Print() const;

		//! Cross product between two vectors
		/*!
		  \param lhs a point
		  \param rhs an other point
		  \return a vector
		*/
		Point CrossProd(const Point& rhs) const;

		//! Vector norm
		/*!
		  \return norm/distance
		*/
		float SquareNorm() const;

		//! Normalize the vector
		/*!
		  \return normalized vector
		*/
		Point Normalize();


		//! addition overload operator between two points (or vectors)
		/*!
		  \param rhs a point
		  \return the addition in space of two points (or vectors) : this + rhs
		*/
		const Point& operator+= (const Point& rhs)
		{
			set(x_ + rhs.x(), y_ + rhs.y(), z_ + rhs.z());
			return *this;
		}

		Point operator+ (const Point& rhs) const
		{
			return Point(x_ + rhs.x(), y_ + rhs.y(), z_ + rhs.z());
		}

		//! substraction overload operator between two points (or vectors)
		/*!
		  \param rhs a point
		  \return the substraction in space of two points (or vectors) : this - rhs
		*/
		const Point& operator-= (const Point& rhs)
		{
			set(x_ - rhs.x(), y_ - rhs.y(), z_ - rhs.z());
			return *this;
		}

		Point operator- (const Point& rhs) const
		{
			return Point(x_ - rhs.x(), y_ - rhs.y(), z_ - rhs.z());
		}

		//! Scalar product overload (scal on the right)
		/*!
		  \param scal a point
		  \return scalar product
		*/
		Point operator*= (const float scal)
		{
			//return Point(scal * x_, scal * y_, scal * z_ );
			set(scal * x_, scal * y_, scal * z_ );
			return *this;
		}

		Point operator* (const float scal)
		{
			return Point(scal * x_, scal * y_, scal * z_ );
		}


	private:
		float x_; /*!< x coordinate */
		float y_; /*!< z coordinate */
		float z_; /*!< y coordinate */
};

typedef Point Vector; //! A vector is a point (in TYPE sense...)

} // end namespace


//! I/O operator overload outside namespace
std::ostream& operator<< (std::ostream& os, raytracer::Point point);

//! Dot product between two vectors
/*!
  \param  lhs a vector
  \param  rhs an other vector
  \return float
*/
float operator* (const raytracer::Point& lhs,
				 const raytracer::Point& rhs);


//! Cross prod
/*!
  \param  lhs a vector
  \param  rhs an other vector
  \return vector
*/
raytracer::Point operator% (const raytracer::Point& lhs,
							const raytracer::Point& rhs);

//! Scalar product overload (scal on the left)
/*!
  \param scal a point
  \return scalar product
*/
raytracer::Point operator* (const float scal, const raytracer::Point& rhs);

#endif  // INC_RAYTRACER_OBJECTS_POINT_H_
